#if !defined(PERCEPTION_VISUALIZER_H_)
#define PERCEPTION_VISUALIZER_H_

#include <pcl/point_cloud.h>
#include <pcl/visualization/pcl_visualizer.h>

#include <Eigen/Core>
#include <memory>
#include <mutex>
#include <string>
#include <thread>

#include "common/circular_queue.h"
#include "common/perception_defs.h"
#include "common/task.h"
#include "common/utility.h"

namespace autodrive {
namespace perception {

class PerceptionVisualizer final {
 public:
  ~PerceptionVisualizer();
  PerceptionVisualizer(PerceptionVisualizer const&) = delete;
  PerceptionVisualizer& operator=(PerceptionVisualizer const&) = delete;
  PerceptionVisualizer(PerceptionVisualizer&&) = delete;
  PerceptionVisualizer& operator=(PerceptionVisualizer&&) = delete;

 public:
  static constexpr std::uint32_t kMaxViewerBufferSize{8U};

  using ViewerBuffer =
      CircularQueue<PerceptionResult const*, kMaxViewerBufferSize>;

  using ViewerBufferPtr = std::shared_ptr<ViewerBuffer>;

 public:
  /// @brief init
  /// @param err error message
  /// @return if no error return true, otherwise return false
  void Init() noexcept;

  /// @brief get init status
  /// @return init status
  inline bool IsInit() const noexcept { return is_init_; }

  /// @brief get dispose status
  /// @return dispose status
  inline bool IsDispose() const noexcept { return is_dispose_; }

  ///@brief Get instance
  ///@return PerceptionVisualizer instance
  static PerceptionVisualizer& GetInstance() noexcept;

  /// @brief enqueue perception result
  /// @param result PerceptionResult
  void Enqueue(PerceptionResult const& result) noexcept(false);

  /// @brief Close
  void Close() noexcept;

 protected:
  PerceptionVisualizer();

  /// @brief sleep for millisecond
  /// @param milliseconds
  void msleep(std::uint32_t milliseconds) noexcept {
    std::chrono::milliseconds dura{milliseconds};
    std::this_thread::sleep_for(dura);
  }

  /// @brief mian task function
  /// @param processorPtr BevFusionProcessor instance
  static void view_task(PerceptionVisualizer* const) noexcept;

  /// @brief gen pcl viewer
  /// @return visualizer pointer
  pcl::visualization::PCLVisualizer::Ptr gen_pcl_viewer() noexcept(false);

  /// @brief gen image viewer
  /// @return viewer id
  std::string gen_image_viewer() noexcept;

  /// @brief update display images
  /// @param PerceptionResult
  /// @param img out display img
  void update_display_image(PerceptionResult const*, cv::Mat& img) noexcept;

  /// @brief update timestamp
  /// @param vis_ptr vis pointer
  /// @param result_ptr result pointer
  void update_timestamp(pcl::visualization::PCLVisualizer::Ptr vis_ptr,
                        std::uint64_t const& timestamp) noexcept;

  /// @brief update pointcloud source
  /// @param vis_ptr vis pointer
  /// @param result_ptr result pointer
  void update_pcl_source(pcl::visualization::PCLVisualizer::Ptr vis_ptr,
                         PointCloudSource const* pcl_source_ptr) noexcept;

 private:
  ///@brief init flag
  bool is_init_;
  /// @brief  init call once flag
  std::once_flag init_flag_;
  /// @brief sync lock
  std::mutex lock_;
  ///@brief is dispose
  bool is_dispose_;
  /// @brief close flag
  bool is_close_;
  ///@brief data frame buffer
  ViewerBufferPtr viewer_buffer_ptr_;
  ///@brief visualize main task
  std::thread viewer_task_;

  /// @brief  display image task
  Task<void> display_image_task_;

  pcl::PointCloud<pcl::PointXYZRGB>::Ptr point_cloud_source_ptr_;
};

}  // namespace perception
}  // namespace autodrive

#endif  // PERCEPTION_VISUALIZER_H_
